local function close_redis(red)
    if not red then
        return
    end
    -- 释放连接（连接池实现）
    local pool_max_idel_time = 10000 --毫秒
    local pool_size = 100 -- 连接池大小
    local ok , err = red:set_keepalive(pool_max_idel_time,pool_size)
    if not ok then
        ngx.say("set keepalive error:",err)
    end
end

local function errlog(...)
    ngx.log(ngx.ERR,"redis: ",...)
end

local function buglog(...)
    ngx.log(ngx.DEBUG,"redis: ",...)
end

local function getIP()
    local headers = ngx.req.get_headers()
    local myIP = headers["X-Real-IP"]
    if myIP == nil then
        myIP = headers["x_forwarded_for"]
    end
    if myIP == nil then
        myIP = ngx.var.remote_addr
    end
    return myIP
end


local cip = getIP();
local shared_ip_blacklist = ngx.shared.shared_ip_blacklist
local ip_block_time=60 --封禁IP时间（秒）
local ip_time_out=30    --指定ip访问频率时间段（秒）
local ip_max_count=20 --指定ip访问频率计数最大值（秒）


if shared_ip_blacklist:get(cip) then
    return ngx.exit(403)
end

local redis = require("resty.redis") -- 引入redis模块
local red = redis:new() -- 创建一个对象，注意使用冒号

--设置超时,毫秒
red:set_timeout(1000)
--建立连接
local ip = "172.16.238.100"
local port = 6379

local ok,err = red:connect(ip,port)
if not ok then
    close_redis(red)
    errlog("Cannot connect: ",err);
    return ngx.exit(ngx.HTTP_INTERNAL_SERVER_ERROR)
end

-- 如果不需要认证，此段可以省略
local count,err = red:get_reused_times()
if count == 0 then -- 新建连接需要认证密码，如果为非0，就说明是从连接池中获取的连接，无需认证
    ok,err = red:auth("123456")
    if not ok then
        errlog("failed to auth : ",err)
        return ngx.exit(ngx.HTTP_INTERNAL_SERVER_ERROR)
    end
elseif err then
    errlog("failed to get reused times: ",err)
    return ngx.exit(ngx.HTTP_INTERNAL_SERVER_ERROR)
end

local key = "limit:frequency:login"..ngx.var.remote_addr

--得到此客户端ip的频次

local resp,err = red:get(key)
if not resp then
    close_redis(red)
    errlog("get redis data error: ",err)
    return ngx.exit(ngx.HTTP_INTERNAL_SERVER_ERROR) -- redis获取值失败
end

if resp == ngx.null then
    red:set(key,1) --第一次访问
    red:expire(key,ip_time_out) -- 10秒过期
end

if type(resp) == "string" then
    if tonumber(resp) > ip_max_count then
        -- 超过10次
        close_redis(red)
        shared_ip_blacklist:set(cip,true,ip_block_time)
        return ngx.exit(429) --直接返回403
    end
end

-- 调用API设置key
ok,err = red:incr(key)
if not ok then
    errlog("redis incr error: ",err)
    close_redis(red)
    return ngx.exit(ngx.HTTP_INTERNAL_SERVER_ERROR) -- redis报错
end

close_redis(red)